/** \file    bRGB.h
  * \brief   8-bit per channel color.
  * \date    1998-2001
  * \author  Aloudin (Albert) Akhriev
  * \version $Header: /home/cscom1r4/cvsroot/cs/include/imglib/bRGB.h,v 1.1 2004/02/04 14:43:23 cscom1r4 Exp $
  */

#ifndef _bRGB_h_
#define _bRGB_h_

#include "cinline.h"
#include "assert.h"
#ifdef MMX
#include "mmintrin.h"
#endif

namespace img {

//=================================================================================================
// Color vector with byte type coordinates (Windows order: Blue - Green - Red).
//=================================================================================================
struct bRGB
{
  unsigned char b;      // blue
  unsigned char g;      // green
  unsigned char r;      // red
  unsigned char a;      // alpha

  bRGB();
  bRGB(unsigned color);
  bRGB(unsigned R, unsigned G, unsigned B, unsigned A = 0);
  bool operator ==( const bRGB &x ) const { return rgbInt() == x.rgbInt(); }
  bool operator !=( const bRGB &x ) const { return rgbInt() != x.rgbInt(); }

  bRGB & Set(unsigned R, unsigned G, unsigned B, unsigned A = 0);

  unsigned char operator[](int n) const;
  unsigned char & operator[](int n);

  int SqDistance(bRGB x) const;
#ifdef MMX
  //before and and after this function call you should issue _mm_empty intrinsic
  int SqDistanceMMX(bRGB x) const;
#endif
  int SqLength() const;
  int operator&(bRGB x) const;

  bRGB & operator=(unsigned color);
  int Int() const;
  int& Int();
  int rgbInt() const; ///< color representation with alpha=0
};

//typedef  std::vector<bRGB>  bRGBArr;

////////////////////////////////////////////////////////////////////////////
// Implementation
////////////////////////////////////////////////////////////////////////////

inline bRGB::bRGB()
{
  assert(sizeof(bRGB) == sizeof(int));
  *((unsigned*)this) = 0;
}


inline bRGB::bRGB(unsigned color)
{
  assert(sizeof(bRGB) == sizeof(int));
  *((unsigned*)this) = color;
}


inline bRGB::bRGB(unsigned R, unsigned G, unsigned B, unsigned A)
: r( (unsigned char) R ),
  g( (unsigned char) G ),
  b( (unsigned char) B ),
  a( (unsigned char) A )
{
  assert(sizeof(bRGB) == sizeof(int));
}


inline bRGB & bRGB::Set(unsigned R, unsigned G, unsigned B, unsigned A)
{
  r = (unsigned char) R;
  g = (unsigned char) G;
  b = (unsigned char) B;
  a = (unsigned char) A;
  return (*this);
}


inline unsigned char bRGB::operator[](int n) const
{
  switch(n)
  {
    case 0: return r;
    case 1: return g;
    case 2: return b;
    case 3: return a;
  }
  assert(0);
  return 0;
}


inline unsigned char & bRGB::operator[](int n)
{
  switch(n)
  {
    case 0: return r;
    case 1: return g;
    case 2: return b;
    case 3: return a;
  }
  assert(0);
  return a;
} 
  

inline bRGB & bRGB::operator=(unsigned color)
{
  *((unsigned*)this) = color;
  return (*this);
}


inline int bRGB::Int() const
{
  return *((int*)this);
}

inline int &bRGB::Int()
{
  return *((int*)this);
}

inline int bRGB::rgbInt() const
{
  return *((int*)this) & 0x00ffffff;
}

inline int bRGB::SqDistance(bRGB x) const
{
  //slow due to DW misalign and many condition jumps
  /*unsigned dr = (r > x.r) ? (r-x.r) : (x.r-r);
  unsigned dg = (g > x.g) ? (g-x.g) : (x.g-g);
  unsigned db = (b > x.b) ? (b-x.b) : (x.b-b);
  
  return (dr*dr + dg*dg + db*db);*/

	unsigned d1 = ((*(unsigned*)this) & 0x000000ff) - ((*(unsigned*)&x) & 0x000000ff);
	unsigned d2 = ((*(unsigned*)this) & 0x0000ff00) - ((*(unsigned*)&x) & 0x0000ff00);
	unsigned d3 = ((*(unsigned*)this) & 0x00ff0000) - ((*(unsigned*)&x) & 0x00ff0000);
	int db = int(d1);
	int dg = short(d2 >> 8);
	int dr = short(d3 >> 16);
	return dr*dr + dg*dg + db*db;
}

#ifdef MMX
inline int bRGB::SqDistanceMMX(bRGB x) const
{
	__m64 a = _mm_cvtsi32_si64( rgbInt() );
	__m64 b = _mm_cvtsi32_si64( x.rgbInt() );
	__m64 z = _mm_setzero_si64();
	a = _mm_unpacklo_pi8( a, z );
	b = _mm_unpacklo_pi8( b, z );
	a = _mm_sub_pi16( a, b );
	a = _mm_madd_pi16( a, a );
	return _mm_cvtsi64_si32(
				_mm_add_pi32( a, _mm_srli_si64( a, 32 )	)
			);
}
#endif

inline int bRGB::SqLength() const
{
  return ( sqr( (unsigned) r ) + 
           sqr( (unsigned) g ) +
           sqr( (unsigned) b ) );
}


inline int bRGB::operator&(bRGB x) const
{
  return ((unsigned)r*(unsigned)x.r +
          (unsigned)g*(unsigned)x.g +
          (unsigned)b*(unsigned)x.b);
}

} //namespace img

#endif //_bRGB_h_
